package com.alibaba.csp.sentinel.dashboard.auth.jwt;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.UUID;

/**
 * 用途描述：JWT和用户信息互转
 *
 * @author YangLong
 * @version V1.0
 * @since 2024/2/26
 */
@Component
public class JwtTokenConverter implements TokenConverter {
    private final Logger log = LoggerFactory.getLogger(JwtTokenConverter.class);
    @Autowired
    private JwtProperties jwtProperties;
    private SecretKey secretKey;
    private static final String LOGIN_NAME = "loginName";
    private static final String NICK_NAME = "nickName";
    private static final String USER_ID = "userId";
    private static final String SUBJECT = "sentinelToken";


    @Override
    public JwtUser convertToken(String token) {
        JwtUser user = null;
        try {
            Jws<Claims> jws = Jwts.parser()
                    .requireIssuer(jwtProperties.getIssuer())
                    .requireSubject(SUBJECT)
                    .verifyWith(secretKey)
                    .build()
                    .parseSignedClaims(token);
            Claims claims = jws.getBody();
            String jwtId = claims.getId();
            String userId = claims.get(USER_ID, String.class);
            String loginName = claims.get(LOGIN_NAME, String.class);
            String nickName = claims.get(NICK_NAME, String.class);
            user = new JwtUser();
            user.setTokenId(jwtId);
            user.setId(userId);
            user.setLoginName(loginName);
            user.setUsername(nickName);
        } catch (JwtException | IllegalArgumentException exception) {
            log.info("decode jwt to user error!", exception);
        }
        return user;
    }

    @Override
    public String convertUser(JwtUser user) {
        try {
            LocalDateTime time = LocalDateTime.now();
            LocalDateTime expire = time.plus(jwtProperties.getExpireTime(), ChronoUnit.MILLIS);
            JwtBuilder builder = Jwts.builder();
            builder.id(UUID.randomUUID().toString().replaceAll("-", ""))
                    .claim(LOGIN_NAME, user.getLoginName())
                    .claim(NICK_NAME, user.getNickName())
                    .claim(USER_ID, user.getId())
                    .subject(SUBJECT)
                    .issuer(jwtProperties.getIssuer())
                    .issuedAt(new Date())
                    .expiration(Date.from(expire.atZone(ZoneId.systemDefault()).toInstant()));
            builder.audience().add(jwtProperties.getAudience());
            return builder.signWith(secretKey).compact();
        } catch (Exception e) {
            log.error("create JWT error!", e);
        }
        return null;

    }

    @PostConstruct
    public void initKey() {
        this.secretKey = Keys.hmacShaKeyFor(jwtProperties.getSecret().getBytes(StandardCharsets.UTF_8));
    }
}
